package com.polaris.live.ui.main.fragment

import android.os.Bundle
import android.view.View
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.polaris.live.R
import com.polaris.live.adapter.home.FirstPlayTopStream
import com.polaris.live.adapter.home.HomeRecommendAdapter
import com.polaris.live.adapter.home.HomeRecommendAdapter.PlayViewHolder
import com.polaris.live.common.cacha.CacheUtil
import com.polaris.live.common.mvvm.base2.BaseVbFragment
import com.polaris.live.common.network.parseState
import com.polaris.live.common.network.state.ResultState
import com.polaris.live.common.util.ToastGravity
import com.polaris.live.common.util.ViewUtils
import com.polaris.live.constant.LiveConst
import com.polaris.live.constant.TrackEventConst
import com.polaris.live.constant.TrackEventConst.TRACK_PAGE
import com.polaris.live.constant.UserConst
import com.polaris.live.databinding.FragmentHomeRecommendBinding
import com.polaris.live.interface_bag.CardStackListenerAdapter
import com.polaris.live.manager.UserStateManager
import com.polaris.live.popup.GlobalPopupManager
import com.polaris.live.resp.back_resp.DisplayUserBean
import com.polaris.live.utils.LiveUtils
import com.polaris.live.utils.StartCallProxy
import com.polaris.live.utils.TrackHelper
import com.polaris.live.utils.block.BlockEventMessage
import com.polaris.live.utils.interfaces.OnAutoRefreshListener
import com.polaris.live.utils.interfaces.OnRefreshStateListener
import com.polaris.live.utils.route.RoutePath
import com.polaris.live.utils.route.Router
import com.polaris.live.utils.state.UserStateUtils
import com.polaris.live.utils.state.copyState
import com.polaris.live.utils.zego.LiveActivityIdentifier
import com.polaris.live.utils.zego.LivePlayer
import com.polaris.live.viewmodel.home.HomeRecommendViewModel
import com.polaris.live.widget.refresh.LoadStateView
import com.yuyakaido.android.cardstackview.CardStackLayoutManager
import com.yuyakaido.android.cardstackview.CardStackView
import com.yuyakaido.android.cardstackview.Direction
import com.yuyakaido.android.cardstackview.StackFrom
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import kotlin.math.min

/**
 * 推荐页
 *
 * @author Created by lucas on 2023/10/16 13:28
 * @since 1.0.0
 */
class HomeRecommendFragment : BaseVbFragment<HomeRecommendViewModel, FragmentHomeRecommendBinding>(),
    OnAutoRefreshListener, FirstPlayTopStream, OnRefreshStateListener {

    private val mAdapter = HomeRecommendAdapter(this)

    private var mLastRefreshTime: Long? = null
    private var mLeaveFlag: Boolean = false

    private lateinit var mPlayHelper: HomeRecommendPlayHelper

    private var mIsFirst = false

    private var mIsHidden = false

    override fun initView(savedInstanceState: Bundle?) {
        EventBus.getDefault().register(this)
        mPlayHelper = HomeRecommendPlayHelper(
            this,
            mViewModel,
            mBinding.cardStack,
            mAdapter
        )

        mBinding.call.apply {
            setViewModel(mViewModel)
            setPage(UserConst.From.RECOMMEND)
            setFragment(this@HomeRecommendFragment)
            setActivity(requireActivity())
            setLastPage(UserConst.LastFrom.RECOMMEND)
        }
        mBinding.cardStack.apply {
            layoutManager =
                CardStackLayoutManager(requireContext(), mCardStackListenerAdapter).apply {
                    setDirections(Direction.VERTICAL)
                    setCanScrollHorizontal(false)
                    setStackFrom(StackFrom.Bottom)
                }
            adapter = mAdapter
            itemAnimator = null
        }

        mViewModel.listLiveData.observe(this) { state ->
            handleResultState(state) {
                mAdapter.addData(it)
            }
        }
        mViewModel.refreshLiveData.observe(this) { state ->
            handleResultState(state) {
                mAdapter.resetData(it)
            }
        }
        mViewModel.itemLiveData.observe(this) { (index, item) ->
            if (mPlayHelper.currentPosition == index
                && mPlayHelper.currentItem?.base?.userId == item.base.userId
                && mPlayHelper.currentItem?.playStreamId != item.playStreamId
            ) {
                mAdapter.setData(index, item)

                mPlayHelper.playTopStream(index)
            }
        }

        mAdapter.setOnItemChildClickListener { _, view, position ->
            val item = mAdapter.getItem(position)
            when (view.id) {

                R.id.bg -> {
                    GlobalPopupManager.showCompletePopup(requireContext()) {
                        Router.builder(RoutePath.USER_SPACE_ACTIVITY)
                            .withInt("page", UserConst.From.RECOMMEND)
                            .withLong("userId", item.base.userId)
                            .withAny("userBasic", item.base)
                            .navigation(requireActivity())
                    }
                }

                R.id.bg_call -> {
                    StartCallProxy.builder(this).startCall(
                        mViewModel,
                        UserConst.From.RECOMMEND,
                        UserConst.LastFrom.RECOMMEND,
                        LiveConst.SenderFrom.MANUAL,
                        item.base.userId,
                        item.base
                    )
                }

                R.id.video -> {
                    GlobalPopupManager.showCompletePopup(requireContext()) {
                        ViewUtils.getAliveTopCompatActivity()?.also {
                            if (item.base.liveType == LiveConst.Type.ONLY_LIVE) {
                                LiveUtils.toSpectator(item).navigation(it)
                            } else {
                                StartCallProxy.builder(it).startCall(
                                    mViewModel,
                                    UserConst.From.RECOMMEND,
                                    UserConst.LastFrom.RECOMMEND,
                                    LiveConst.SenderFrom.MANUAL,
                                    item.base.userId,
                                    item.base
                                )
                            }
                        }
                    }
                }
            }
        }

        UserStateManager.observe(this, {
            getDisplayUserIds()
        }, { messages ->
            val indices = getDisplayIndices() ?: return@observe

            if (mLeaveFlag) {
                mLeaveFlag = false

                val offlineState = messages.find { message ->
                    UserStateUtils.resolveUserState(message.state).let {
                        it == UserStateUtils.USER_STATE_OFFLINE || it == UserStateUtils.USER_STATE_BUSY
                    }
                }
                if (offlineState != null) {
                    mViewModel.refreshRecommendList(mAdapter.data.lastOrNull()?.base?.userId)
                    return@observe
                }
            }

            messages.forEach { message ->
                val state = message.state

                for (index in indices) {
                    val item = mAdapter.getItem(index)
                    if (item.base.userId == state.userId) {
                        val newBean = mAdapter.data[index].let {
                            it.copy(base = it.base.copyState(message))
                        }

                        mAdapter.setData(index, newBean)

                        //如果当前是则需要做处理
                        if (index == mPlayHelper.currentPosition) {
                            if (state.living == 1) {
                                //之前是关播，并且streamId不一致，说明需要重新获取数据播放
                                if (item.base.living == 0) {
                                    mViewModel.getDisplayUser(index, newBean.base.userId)
                                }
                            } else {
                                mPlayHelper.stopCurrentHolder()
                            }
                        }

                        break
                    }
                }
            }
        })
    }

    private fun handleResultState(
        state: ResultState<List<DisplayUserBean>>,
        block: (List<DisplayUserBean>) -> Unit,
    ) {
        mLeaveFlag = false

        parseState(state, {
            if (it.isEmpty()) {
                mBinding.svLoad.setState(LoadStateView.STATE_EMPTY)
            } else {
                mBinding.svLoad.setState(LoadStateView.STATE_SUCCESS)
            }

            block(it)
        }, {
            if (mPlayHelper.isEmpty()) {
                mBinding.svLoad.setState(LoadStateView.STATE_ERROR, it.errorMsg)
            } else {
                ToastGravity.showGravity(it.errorMsg)
            }
        })

        mLastRefreshTime = CacheUtil.getServerTime()
    }

    private fun getDisplayUserIds(): List<Long> {
        val indices = getDisplayIndices()
            ?: return emptyList()

        return mAdapter.data.subList(indices.first, indices.last + 1).map {
            it.base.userId
        }
    }

    private fun getDisplayIndices(): IntRange? {
        if (mAdapter.data.isEmpty()) {
            return null
        }

        val endPosition = mAdapter.data.size - 1
        val topPosition = min(mPlayHelper.getTopPosition(), endPosition)

        return IntRange(topPosition, endPosition)
    }

    override fun initData() {
        mViewModel.getRecommendList()
    }

    private val mCardStackListenerAdapter = object : CardStackListenerAdapter() {
        override fun onCardAppeared(view: View?, position: Int) {
            loadMore(position)
            //加载新的时候需要开始播放
            mPlayHelper.playTopStream(position)
        }

        override fun onCardSwiped(direction: Direction?) {
            //如果已经是最后一张取消，则直接刷新
            if (mPlayHelper.isEmpty()) {
                mViewModel.refreshRecommendList(mPlayHelper.currentItem?.base?.userId)
            }
        }
    }

    private fun loadMore(topPosition: Int = mPlayHelper.getTopPosition()) {
        if (mAdapter.data.size - topPosition <= 3) {
            mViewModel.getRecommendList(mPlayHelper.currentItem?.base?.userId)
        }
    }

    override fun getCurrentPosition(): Int {
        return mPlayHelper.currentPosition
    }

    override fun player(holder: PlayViewHolder, item: DisplayUserBean) {
        //首次加载播放
        mPlayHelper.playTopStream(0, holder, item)
    }

    override fun onAutoRefresh() {
        if (!isCreated()) {
            return
        }
        loadMore()
    }

    override fun onPause() {
        super.onPause()
        mPlayHelper.onHiddenChanged(true)
    }

    override fun onResume() {
        super.onResume()
        if (!mIsFirst) {
            mIsFirst = true
            TrackHelper.trackFragment(this, TrackEventConst.EVENT_HOMEPAGE_UC, TRACK_PAGE)
        }

        if (!mIsHidden) {
            mPlayHelper.onHiddenChanged(false)
        }
        if (!mIsHidden) {
            toRefresh()
        }
    }

    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        mIsHidden = hidden
        if (!isCreated()) {
            return
        }
        mPlayHelper.onHiddenChanged(hidden)

        if (!hidden) {
            toRefresh()
        }
    }

    private fun toRefresh() {
        if (isLoadData() && !mBinding.svLoad.isLoading()) {
            val lastRefreshTime = mLastRefreshTime
            if (lastRefreshTime == null || CacheUtil.getServerTime() - lastRefreshTime > 60 * 1000) {
                mViewModel.refreshRecommendList(mAdapter.data.lastOrNull()?.base?.userId)
            } else {
                mLeaveFlag = true
            }
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onVersionUpdateEvent(event: BlockEventMessage) {
        val iterator = mAdapter.data.iterator()
        while (iterator.hasNext()) {
            val item = iterator.next()
            if (item.base.userId == event.userId) {
                iterator.remove()
            }
        }
        mAdapter.notifyDataSetChanged()
    }

    override fun onDestroy() {
        super.onDestroy()
        EventBus.getDefault().unregister(this)
        if (::mPlayHelper.isInitialized) {
            mPlayHelper.resetCurrentHolder()
        }
    }

    override fun onRefreshState() {
        if (!isCreated()) return
        mBinding.call.anewView()
    }
}

internal class HomeRecommendPlayHelper(
    private val owner: LifecycleOwner,
    private val vm: HomeRecommendViewModel,
    private val cardStack: CardStackView,
    private val adapter: HomeRecommendAdapter,
) {

    var currentPosition: Int = -1
    var currentHolder: PlayViewHolder? = null
    var currentItem: DisplayUserBean? = null

    private var stopPlayJob: Job? = null

    fun getLayoutManager(): CardStackLayoutManager {
        return cardStack.layoutManager as CardStackLayoutManager
    }

    fun getTopPosition(): Int {
        return getLayoutManager().topPosition
    }

    fun isEmpty(): Boolean {
        //如果当前顶部与最大大小一致则认为当前没有数据
        return adapter.data.isEmpty() || getTopPosition() == adapter.data.size
    }

    fun playTopStream(topPosition: Int) {
        val holder = cardStack.findViewHolderForLayoutPosition(topPosition) as? PlayViewHolder
        val item = adapter.getItemOrNull(topPosition)
        if (holder == null || item == null) {
            return
        }
        if (currentPosition == topPosition
            && currentItem?.playStreamId == item.playStreamId
        ) {
            return
        }

        playTopStream(topPosition, holder, item)
    }

    fun playTopStream(position: Int, holder: PlayViewHolder, item: DisplayUserBean) {
        stopCurrentHolder()

        currentHolder = holder
        currentItem = item
        currentPosition = position

        startCurrentHolder(holder, item)

        UserStateManager.updateUserObserve(owner, listOf(item.base.userId))
    }

    private fun startCurrentHolder(
        holder: PlayViewHolder? = currentHolder,
        item: DisplayUserBean? = currentItem,
    ) {
        item?.let {
            if (it.playStreamId != null) {
                holder?.let { ho ->
                    LivePlayer.startLive(
                        it.playStreamId, ho.binding.video,
                        LiveActivityIdentifier.HOME_RECOMMEND_FRAGMENT, true, ho.listener
                    )
                }
            }
        }
    }

    fun stopCurrentHolder() {
        currentHolder?.let { ho ->
            LivePlayer.stopLive(ho.binding.video)
        }
    }

    private fun cancelStopPlayJob() {
        stopPlayJob?.cancel()
        stopPlayJob = null
    }

    fun resetCurrentHolder() {
        stopCurrentHolder()

        currentItem = null
        currentHolder = null
    }

    fun onHiddenChanged(hidden: Boolean) {
        cancelStopPlayJob()
        if (hidden) {
            stopPlayJob = vm.viewModelScope.launch {
                try {
                    delay(1000)
                    stopCurrentHolder()
                } finally {
                    if (stopPlayJob == coroutineContext[Job]) {
                        stopPlayJob = null
                    }
                }
            }
        } else {
            startCurrentHolder()
        }
    }
}